<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module" src="../../lib/mixins/properties-mixin.js"></script>
  <script type="module" src="../../lib/mixins/property-accessors.js"></script>
<body>

  <dom-module id="my-element">
    <script type="module">
import { PropertiesMixin } from '../../lib/mixins/properties-mixin.js';
import { PropertyAccessors } from '../../lib/mixins/property-accessors.js';

class MyElement extends PropertyAccessors(PropertiesMixin(HTMLElement)) {

  static get properties() {
    return {
      prop: String
    };
  }

  constructor() {
    super();
    this._propertiesChanged = sinon.spy();
    this._tapListener = sinon.spy();
    this._calledConstructor++;
    this.prop = 'prop';
  }

  connectedCallback() {
    super.connectedCallback();
    this._calledConnectedCallback++;
  }

  attributeChangedCallback() {
    super.attributeChangedCallback.apply(this, arguments);
    this._callAttributeChangedCallback++;
  }

  ready() {
    super.ready();
    this._ensureAttribute('tabIndex', 0);
    this.addEventListener('click', (e) => this._tapListener(e));
    this._calledReady++;
  }

}

MyElement.prototype._calledConstructor = 0;
MyElement.prototype._calledConnectedCallback = 0;
MyElement.prototype._calledReady = 0;
MyElement.prototype._callAttributeChangedCallback = 0;

customElements.define('my-element', MyElement);

window.MyElement = MyElement;
</script>
  </dom-module>


  <dom-module id="sub-element">
    <script type="module">
import '../../lib/mixins/properties-mixin.js';
import '../../lib/mixins/property-accessors.js';

class SubElement extends window.MyElement {

  static get properties() {
    return {
      prop2: String
    };
  }

  constructor() {
    super();
    this._calledConstructor++;
    this.prop = 'sub';
    this.prop2 = 'prop2';
  }

  connectedCallback() {
    super.connectedCallback();
    this._calledConnectedCallback++;
  }

  ready() {
    super.ready();
    this._calledReady++;
    this._ensureAttribute('role', 'button');
    this.addEventListener('click', (e) => this._tapListener(e));
  }

}

customElements.define('sub-element', SubElement);

window.SubElement = SubElement;
</script>
  </dom-module>

  <dom-module id="sub-mixin-element">
    <script type="module">
import '../../lib/mixins/properties-mixin.js';
import '../../lib/mixins/property-accessors.js';

function MyMixin(Base) {
  return class extends Base {

    static get properties() {
      return {
        mixin: Number
      };
    }

    constructor() {
      super();
      this._calledConstructor++;
      this.mixin = 'mixin';
    }

    connectedCallback() {
      super.connectedCallback();
      this._calledConnectedCallback++;
    }

    ready() {
      super.ready();
      this._calledReady++;
      this._ensureAttribute('mixinattr', 'mixinattr');
      this.addEventListener('click', (e) => this._tapListener(e));
    }

  };
}

class SubMixinElement extends MyMixin(window.SubElement) {

  static get properties() {
    return {
      prop3: String,
      camelCase: String
    };
  }

  constructor() {
    super();
    this._calledConstructor++;
    this.prop3 = 'prop3';
  }

  connectedCallback() {
    super.connectedCallback();
    this._calledConnectedCallback++;
  }

  ready() {
    super.ready();
    this._calledReady++;
    this._ensureAttribute('submixinattr', 'submixinattr');
    this.addEventListener('click', (e) => this._tapListener(e));
  }

}

customElements.define('sub-mixin-element', SubMixinElement);

window.SubMixinElement = SubMixinElement;
</script>
  </dom-module>

  <test-fixture id="my-element-attr">
    <template>
      <my-element prop="attr"></my-element>
    </template>
  </test-fixture>

  <test-fixture id="sub-element-attr">
    <template>
      <sub-element prop="attr" prop2="attr"></sub-element>
    </template>
  </test-fixture>

  <test-fixture id="sub-mixin-element-attr">
    <template>
      <sub-mixin-element prop="attr" prop2="attr" prop3="attr" mixin="5" camel-case="camelCase"></sub-mixin-element>
    </template>
  </test-fixture>

  <script type="module">
import '../../lib/mixins/properties-mixin.js';
import '../../lib/mixins/property-accessors.js';
suite('class extends Polymer.PropertiesMixin', function() {

  let el;

  setup(function() {
    el = document.createElement('my-element');
    document.body.appendChild(el);
  });

  teardown(function() {
    document.body.removeChild(el);
  });

  test('instanceof', function() {
    assert.instanceOf(el, HTMLElement);
    assert.instanceOf(el, window.MyElement);
  });

  test('lifecycle', function() {
    assert.equal(el._calledConstructor, 1);
    assert.equal(el._calledConnectedCallback, 1);
    assert.equal(el._calledReady, 1);
    assert.equal(el._callAttributeChangedCallback, 0);
  });

  test('listeners', function() {
    el.dispatchEvent(new CustomEvent('click'));
    assert.equal(el._tapListener.callCount, 1);
  });

  test('properties', function() {
    assert.equal(el.prop, 'prop');
    assert.isTrue(el._propertiesChanged.calledOnce);
    assert.deepEqual(el._propertiesChanged.args[0][0], {prop: 'prop'});
  });

  test('attributes', function() {
    const fixtureEl = fixture('my-element-attr');
    assert.equal(fixtureEl.prop, 'attr');
    assert.equal(fixtureEl._callAttributeChangedCallback, 1);
    assert.isTrue(fixtureEl.hasAttribute('tabindex'));
  });

});

suite('subclass', function() {

  let el;

  setup(function() {
    el = document.createElement('sub-element');
    document.body.appendChild(el);
  });

  teardown(function() {
    document.body.removeChild(el);
  });

  test('instanceof', function() {
    assert.instanceOf(el, HTMLElement);
    assert.instanceOf(el, window.MyElement);
    assert.instanceOf(el, window.SubElement);
  });

  test('lifecycle', function() {
    assert.equal(el._calledConstructor, 2);
    assert.equal(el._calledConnectedCallback, 2);
    assert.equal(el._calledReady, 2);
    assert.equal(el._callAttributeChangedCallback, 0);
  });

  test('listeners', function() {
    el.dispatchEvent(new CustomEvent('click'));
    assert.equal(el._tapListener.callCount, 2);
  });

  test('properties', function() {
    assert.equal(el.prop, 'sub');
    assert.equal(el.prop2, 'prop2');
    assert.isTrue(el._propertiesChanged.calledOnce);
    assert.deepEqual(el._propertiesChanged.args[0][0], {prop: 'sub', prop2: 'prop2'});
  });

  test('attributes', function() {
    const fixtureEl = fixture('sub-element-attr');
    assert.equal(fixtureEl.prop, 'attr');
    assert.equal(fixtureEl.prop2, 'attr');
    assert.equal(fixtureEl._callAttributeChangedCallback, 2);
    assert.isTrue(fixtureEl.hasAttribute('tabindex'));
    assert.equal(fixtureEl.getAttribute('role'), 'button');
  });

});

suite('mixin', function() {

  let el;

  setup(function() {
    el = document.createElement('sub-mixin-element');
    document.body.appendChild(el);
  });

  teardown(function() {
    document.body.removeChild(el);
  });

  test('instanceof', function() {
    assert.instanceOf(el, HTMLElement);
    assert.instanceOf(el, window.MyElement);
    assert.instanceOf(el, window.SubElement);
    assert.instanceOf(el, window.SubMixinElement);
  });

  test('lifecycle', function() {
    assert.equal(el._calledConstructor, 4);
    assert.equal(el._calledConnectedCallback, 4);
    assert.equal(el._calledReady, 4);
    assert.equal(el._callAttributeChangedCallback, 0);
  });

  test('listeners', function() {
    el.dispatchEvent(new CustomEvent('click'));
    assert.equal(el._tapListener.callCount, 4);
  });

  test('properties', function() {
    assert.equal(el.prop, 'sub');
    assert.equal(el.prop2, 'prop2');
    assert.equal(el.mixin, 'mixin');
    assert.equal(el.prop3, 'prop3');
    assert.isTrue(el._propertiesChanged.calledOnce);
    assert.deepEqual(el._propertiesChanged.args[0][0], {prop: 'sub', prop2: 'prop2', mixin: 'mixin', prop3: 'prop3'});
  });

  test('attributes', function() {
    const fixtureEl = fixture('sub-mixin-element-attr');
    assert.strictEqual(fixtureEl.prop, 'attr');
    assert.strictEqual(fixtureEl.prop2, 'attr');
    assert.strictEqual(fixtureEl.prop3, 'attr');
    assert.strictEqual(fixtureEl.camelCase, 'camelCase');
    assert.strictEqual(fixtureEl.mixin, 5);
    assert.equal(fixtureEl._callAttributeChangedCallback, 5);
    assert.isTrue(fixtureEl.hasAttribute('tabindex'));
    assert.equal(fixtureEl.getAttribute('role'), 'button');
    assert.equal(fixtureEl.getAttribute('mixinattr'), 'mixinattr');
  });

});
</script>
</body>
</html>
